/**
 * Copyright (C) 2009 Ignasi Fosch Alonso
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
 * details.
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * You can reach me by mail at natx@y10k.ws
 * You can reach him by paper mail at:
 * C. LLEIDA, 41-43, 3r 1a
 * El Prat de Llobregat 08820
 * Barcelona, SPAIN
 */
package ws.y10k.vsmanager;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Iterator;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;

import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.vmware.vim25.InvalidProperty;
import com.vmware.vim25.RuntimeFault;

import ws.y10k.swt.utils.ShellUtils;
import ws.y10k.vi.Host;
import ws.y10k.vi.HostAlreadyExistsException;
import ws.y10k.vi.HostDataEmptyException;
import ws.y10k.vi.HostTable;
import ws.y10k.vi.VIVersionNotSupported;

/**
 * @author natx
 *
 */
public class VSManager extends ApplicationWindow {
	
	private static HostTable hctHosts;
	private static TreeViewer trvHosts;
	private static CTabFolder tbfMain;
	private static Options optMain;
	private static ObjectContainer db;
	private Shell shlInit;
	private Label lblLoadStatus;
	private ProgressBar pbrLoad;
	private String[] args;
	static Image host;
	static Image maintenanceMode;
	static Image hostMaintenanceMode;
	static Image hostStandby;
	static Image hostUnknown;
	static Image guest;
	static Image guestPoweredOn;
	static Image guestSuspended;

	public static void save(Object guest) {
		db.store(guest);
	}
	
	static void addHost(Object hstNew) throws HostAlreadyExistsException {
		db.store(hstNew);
		hctHosts.add(hstNew);
		trvHosts.refresh();
	}
	
	public void checkArgs() {
		// TODO i18n
		lblLoadStatus.setText("Checking arguments...");
		lblLoadStatus.redraw();
		lblLoadStatus.update();
		optMain = new Options();
		CmdLineParser parser = new CmdLineParser(optMain);
		try {
			parser.parseArgument(args);
			pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 3 / 100);
			if ( optMain.DBFileName == null ) optMain.DBFileName = "vsmanager.db";
			pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 3 / 100);
			if ( optMain.FilesPath == null ) {
				optMain.FilesPath = System.getProperty("user.home") + System.getProperty("file.separator") + ".vsmanager";
				File DBFileDir = new File(optMain.FilesPath);
				try {
					DBFileDir.mkdirs();
				} catch (SecurityException se) {
					MessageDialog.openError(shlInit.getShell(), "Cannot create application directory " + optMain.FilesPath, "This application needs a directory to write data. It's trying to create " + optMain.FilesPath + " directory but system claimed a security problem. Please check permissions, or ask your system administrator to check them, or specify another path to store files (using -filespath)");
				}
			}
			pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 4 / 100);
		} catch (CmdLineException e) {
			// TODO Z: Check why this should happen
			e.printStackTrace();
		}
	}
	
	public boolean checkInstalled() {
		// TODO i18n
		// TODO after i18n: If locale files aren't present, it should mark install to be done.
		boolean installed = true;

		lblLoadStatus.setText("Checking installation...");
		lblLoadStatus.redraw();
		lblLoadStatus.update();
		optMain.DBFile = new File(optMain.FilesPath + System.getProperty("file.separator") + optMain.DBFileName);
		if ( !optMain.DBFile.exists() ) installed = false;
		pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 10 / 100);
		
		return installed;
	}

	public void doInstall() {
		// TODO i18n
		if ( optMain.install || !checkInstalled() ) {
			lblLoadStatus.setText("Installing...");
			lblLoadStatus.redraw();
			lblLoadStatus.update();
			db = Db4o.openFile(Db4o.newConfiguration(),optMain.DBFile.getAbsolutePath());
			pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 5 / 100);
			db.close();
			pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 5 / 100);
		} else {
			pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 10 / 100);
		}
	}
	
	public void openDB() {
		// TODO i18n
		lblLoadStatus.setText("Opening DB...");
		lblLoadStatus.redraw();
		lblLoadStatus.update();
		db = Db4o.openFile(Db4o.newConfiguration(),optMain.DBFile.getAbsolutePath());
		pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 10 / 100);
	}
	
	@SuppressWarnings("unchecked")
	public void loadData() {
		// TODO i18n
		openDB();
		lblLoadStatus.setText("Loading data from DB...");
		lblLoadStatus.redraw();
		lblLoadStatus.update();
		ObjectSet result = db.queryByExample(Host.class);
		int size = result.size();
		while ( result.hasNext() ) {
			try {
				final Host hstRestored = (Host)result.next();
				hctHosts.add(hstRestored);
				pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * (25 / size) / 100);
			} catch (HostAlreadyExistsException e) {
				// TODO Z: Check why this should happen
				e.printStackTrace();
			}
		}
	}
	
	public void connectHosts() {
		// TODO i18n
		lblLoadStatus.setText("Connecting to hosts...");
		lblLoadStatus.redraw();
		lblLoadStatus.update();
		try {
			final Object[] hosts = hctHosts.getChildren();
			for ( int i = 0; i < hosts.length; i++ ) {
				final int size = hosts.length;
				final String hostFQDN = ((Host) hosts[i]).getFQDN();
				try {
					shlInit.getDisplay().syncExec( new Runnable() {
						public void run() {
								final Host host = (Host) hctHosts.getChildren(hostFQDN);
								try {
									host.connect();
								} catch (RemoteException e) {
									// TODO Z: Check why this should happen
									e.printStackTrace();
								} catch (MalformedURLException e) {
									// TODO Z: Check why this should happen
									e.printStackTrace();
								} catch (HostDataEmptyException e) {
									// TODO Z: Check why this should happen
									e.printStackTrace();
								} catch (VIVersionNotSupported e) {
									// TODO Z: Check why this should happen
									e.printStackTrace();
								}
								pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * (30 / size) / 100);
						}
					});
				} catch (NullPointerException ne) {
					ne.printStackTrace();
				}
			}
		} catch (InvalidProperty e) {
			// TODO Z: Check why this should happen
			e.printStackTrace();
		} catch (RuntimeFault e) {
			// TODO Z: Check why this should happen
			e.printStackTrace();
		} catch (RemoteException e) {
			// TODO Z: Check why this should happen
			e.printStackTrace();
		}
	}
	
	public void loadImages() {
		// TODO i18n
		lblLoadStatus.setText("Loading images and icons...");
		lblLoadStatus.redraw();
		lblLoadStatus.update();
		try {
			host = new Image(null,new FileInputStream("icons/host.png"));
		} catch (FileNotFoundException e) {
			host = new Image(null,10,10);
			GC gc = new GC(host);
			gc.setBackground(new Color(null, 255, 0, 0));
			gc.fillRectangle(0,0,10,10);
			gc.dispose();
		}
		pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 1 / 100);
		try {
			maintenanceMode = new Image(null,new FileInputStream("icons/maintenanceMode.png"));
		} catch (FileNotFoundException e) {
			maintenanceMode = new Image(null,10,10);
			GC gc = new GC(maintenanceMode);
			gc.setBackground(new Color(null, 255, 0, 0));
			gc.fillRectangle(0,0,10,10);
			gc.dispose();
		}
		pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 1 / 100);
		try {
			hostMaintenanceMode = new Image(null,new FileInputStream("icons/hostMaintenanceMode.png"));
		} catch (FileNotFoundException e) {
			hostMaintenanceMode = new Image(null,10,10);
			GC gc = new GC(hostMaintenanceMode);
			gc.setBackground(new Color(null, 255, 0, 0));
			gc.fillRectangle(0,0,10,10);
			gc.dispose();
		}
		pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 1 / 100);
		try {
			hostStandby = new Image(null,new FileInputStream("icons/hostStandby.png"));
		} catch (FileNotFoundException e) {
			hostStandby = new Image(null,10,10);
			GC gc = new GC(hostStandby);
			gc.setBackground(new Color(null, 255, 0, 0));
			gc.fillRectangle(0,0,10,10);
			gc.dispose();
		}
		pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 1 / 100);
		try {
			hostUnknown = new Image(null,new FileInputStream("icons/hostUnknown.png"));
		} catch (FileNotFoundException e) {
			hostUnknown = new Image(null,10,10);
			GC gc = new GC(hostUnknown);
			gc.setBackground(new Color(null, 255, 0, 0));
			gc.fillRectangle(0,0,10,10);
			gc.dispose();
		}
		pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 1 / 100);
		try {
			guest = new Image(null,new FileInputStream("icons/guest.png"));
		} catch (FileNotFoundException e) {
			guest = new Image(null,10,10);
			GC gc = new GC(guest);
			gc.setBackground(new Color(null, 255, 0, 0));
			gc.fillRectangle(0,0,10,10);
			gc.dispose();
		}
		pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 1 / 100);
		try {
			guestPoweredOn = new Image(null,new FileInputStream("icons/guestPoweredOn.png"));
		} catch (FileNotFoundException e) {
			guestPoweredOn = new Image(null,10,10);
			GC gc = new GC(guestPoweredOn);
			gc.setBackground(new Color(null, 255, 0, 0));
			gc.fillRectangle(0,0,10,10);
			gc.dispose();
		}
		pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 1 / 100);
		try {
			guestSuspended = new Image(null,new FileInputStream("icons/guestSuspended.png"));
		} catch (FileNotFoundException e) {
			guestSuspended = new Image(null,10,10);
			GC gc = new GC(guestSuspended);
			gc.setBackground(new Color(null, 255, 0, 0));
			gc.fillRectangle(0,0,10,10);
			gc.dispose();
		}
		pbrLoad.setSelection(pbrLoad.getSelection() + pbrLoad.getMaximum() * 1 / 100);
	}
	
	public void init() {
		final ArrayList<String> operations = new ArrayList<String>();
		
		operations.add("checkArgs");
		operations.add("doInstall");
		operations.add("loadData");
		operations.add("connectHosts");
		operations.add("loadImages");
		
		final VSManager vsm = this;
		shlInit = new Shell(SWT.ON_TOP);
		lblLoadStatus = new Label(shlInit, SWT.NONE);
		pbrLoad = new ProgressBar(shlInit, SWT.SMOOTH);
		ShellUtils.setOptimalSize(shlInit, 20);
		ShellUtils.center(shlInit);
		shlInit.setLayout(new GridLayout());
		GridData gdata = new GridData();
		gdata.widthHint = shlInit.getBounds().width - 10;
		gdata.heightHint = shlInit.getBounds().height * 10 / 100;
		lblLoadStatus.setLayoutData(gdata);
		lblLoadStatus.setText("Loading...");
		gdata = new GridData();
		gdata.widthHint = shlInit.getBounds().width - 10;
		gdata.heightHint = shlInit.getBounds().height * 10 / 100;
		pbrLoad.setLayoutData(gdata);
		pbrLoad.setMaximum(100);
		shlInit.pack();
		shlInit.open();
		shlInit.getDisplay().syncExec( new Runnable() {
			@SuppressWarnings("unchecked")
			public void run() {
				Iterator<String> it = operations.iterator();
				while ( it.hasNext() ) {
					String operation = it.next();
					Class params[] = new Class[0];
					Object argList[] = new Object[0];
					try {
						Method m = VSManager.class.getMethod(operation, params);
						m.invoke(vsm, argList);
					} catch (SecurityException e2) {
						// TODO Z: Check why this should happen
						e2.printStackTrace();
					} catch (NoSuchMethodException e2) {
						// TODO Z: Check why this should happen
						e2.printStackTrace();
					} catch (IllegalArgumentException e3) {
						// TODO Z: Check why this should happen
						e3.printStackTrace();
					} catch (IllegalAccessException e4) {
						// TODO Z: Check why this should happen
						e4.printStackTrace();
					} catch (InvocationTargetException e5) {
						// TODO Z: Check why this should happen
						e5.printStackTrace();
					} 
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e1) {
						// TODO Z: Check why this should happen
						e1.printStackTrace();
					}
				}
				shlInit.close();
			}
		});
	}
	
	public Control createContents(Composite parent) {
		// TODO i18n
		ShellUtils.setOptimalSize(parent.getShell(),60);
		ShellUtils.center(parent.getShell());
		parent.getShell().setText("vSphere Manager");
		
		Composite cmpMain = new Composite(parent,SWT.NONE);		
		GridLayout grd = new GridLayout();
		grd.numColumns = 2;
		cmpMain.setLayout(grd);
		
		trvHosts = new TreeViewer(cmpMain, SWT.H_SCROLL | SWT.BORDER);
		trvHosts.setContentProvider(new HostListContentProvider());
		trvHosts.setInput(hctHosts);
		trvHosts.setLabelProvider(new HostListLabelProvider());
		GridData grdData = new GridData();
		grdData.horizontalAlignment = SWT.BEGINNING;
		grdData.widthHint = 300;
		grdData.heightHint = parent.getShell().getBounds().height - 90;
		trvHosts.getTree().setLayoutData(grdData);
		trvHosts.getTree().addListener(SWT.Selection, new HostsTreeSelectionListener());
		
		tbfMain = new CTabFolder(cmpMain, SWT.TOP);
		grdData = new GridData();
		grdData.horizontalAlignment = SWT.END;
		grdData.widthHint = parent.getShell().getBounds().width - 50 - 300;
		grdData.heightHint = parent.getShell().getBounds().height - 105;
		tbfMain.setLayoutData(grdData);
		tbfMain.setLayout(new GridLayout());
		
		CTabItem tbiIntro = new CTabItem(tbfMain,SWT.NONE);
		tbiIntro.setText("Welcome to vSphere Manager");
		
		// TODO This tab should contain a welcome message with some instructions.
		Label lblIntroduction = new Label(tbfMain,SWT.WRAP);
		lblIntroduction.setText("");
		tbiIntro.setControl(lblIntroduction);
		
		tbfMain.setFocus();
		
		return parent;
	}
	
	protected MenuManager createMenuManager() {
		// TODO i18n
		MenuManager mnuBar = new MenuManager();
		
		MenuManager mnuHosts = new MenuManager("&Hosts");
		MenuManager mnuHelp = new MenuManager("&Help");

		Action addHost = new AddHostAction(this);
		Action quit = new QuitAction(this);
		
		mnuHosts.add(addHost);
		mnuHosts.add(new Separator());
		mnuHosts.add(quit);
		
		mnuBar.add(mnuHosts);
		mnuBar.add(mnuHelp);
		
		return mnuBar;
	}
	
	public boolean close() {
		// TODO i18n
		MessageDialog closeConfirmQuestion = new MessageDialog(this.getShell(), "Confirmation", null, "Do you really want to close?", MessageDialog.QUESTION, new String[]{"OK","Cancel"},0);
		closeConfirmQuestion.open();
		if ( closeConfirmQuestion.getReturnCode() == 0 ) {
			db.close();
			super.close();
			return true;
		} else {
			return false;
		}
	}

	static CTabItem addTabItem(String title) {
		
		CTabItem tbi = null;
		CTabItem tbis[] = tbfMain.getItems();
		for ( int i = 0; i < tbis.length && tbi == null; i++ ) {
			if ( tbis[i].getText().equals(title) ) {
				tbi = tbis[i];
			}
		}
		if ( tbi == null ) {
			tbi = new CTabItem(tbfMain,SWT.CLOSE,0);
			tbi.setText(title);
		}
		tbfMain.setSelection(tbi);
		tbfMain.redraw();
		return tbi;
	}
	
	protected VSManager(String[] args) {
		super((Shell) null);	
		this.args = args;
		hctHosts = new HostTable();
		init();
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {		
		VSManager main = new VSManager(args);
		
		main.setBlockOnOpen(true);
		main.addMenuBar();
		main.open();
		Display.getCurrent().dispose();
	}

}
